import { customRef, type CSSProperties, onScopeDispose, getCurrentInstance } from 'vue'

declare global {
  interface CSSStyleSheet {
    __reuse?: any
  }
}

export type CSSPseudoProperty =
  | ':-moz-broken'
  | ':-moz-drag-over'
  | ':-moz-first-node'
  | ':-moz-handler-blocked'
  | ':-moz-handler-crashed'
  | ':-moz-handler-disabled'
  | ':-moz-last-node'
  | ':-moz-loading'
  | ':-moz-locale-dir(ltr)'
  | ':dir(ltr)'
  | ':-moz-locale-dir(rtl)'
  | ':dir(rtl)'
  | ':-moz-only-whitespace'
  | ':-moz-submit-invalid'
  | ':-moz-suppressed'
  | ':-moz-user-disabled'
  | ':-moz-window-inactive'
  | ':active'
  | ':any-link'
  | ':-webkit-autofill'
  | ':autofill'
  | ':blank'
  | ':buffering'
  | ':checked'
  | ':default'
  | ':defined'
  | ':disabled'
  | ':empty'
  | ':enabled'
  | ':first'
  | ':first-child'
  | ':first-of-type'
  | ':focus'
  | ':focus-visible'
  | ':focus-within'
  | ':fullscreen'
  | ':hover'
  | ':in-range'
  | ':indeterminate'
  | ':invalid'
  | ':last-child'
  | ':last-of-type'
  | ':left'
  | ':link'
  | ':local-link'
  | ':modal'
  | ':muted'
  | ':only-child'
  | ':only-of-type'
  | ':optional'
  | ':out-of-range'
  | ':paused'
  | ':picture-in-picture'
  | ':placeholder-shown'
  | ':playing'
  | ':popover-open'
  | ':read-only'
  | ':read-write'
  | ':required'
  | ':right'
  | ':root'
  | ':scope'
  | ':seeking'
  | ':stalled'
  | ':target'
  | ':target-within'
  | ':user-invalid'
  | ':user-valid'
  | ':valid'
  | ':visited'
  | ':volume-locked'
  | '::-moz-color-swatch'
  | '::-moz-focus-inner'
  | '::-moz-list-bullet'
  | '::-moz-list-number'
  | '::-moz-page'
  | '::-moz-page-sequence'
  | '::-moz-progress-bar'
  | '::-moz-range-progress'
  | '::-moz-range-thumb'
  | '::-moz-range-track'
  | '::-moz-scrolled-page-sequence'
  | '::-webkit-inner-spin-button'
  | '::-webkit-meter-bar'
  | '::-webkit-meter-even-less-good-value'
  | '::-webkit-meter-inner-element'
  | '::-webkit-meter-optimum-value'
  | '::-webkit-meter-suboptimum-value'
  | '::-webkit-outer-spin-button'
  | '::-webkit-progress-bar'
  | '::-webkit-progress-inner-element'
  | '::-webkit-progress-value'
  | '::-webkit-scrollbar'
  | '::-webkit-search-cancel-button'
  | '::-webkit-search-results-button'
  | '::-webkit-slider-runnable-track'
  | '::-webkit-slider-thumb'
  | '::after'
  | '::backdrop'
  | '::before'
  | '::cue'
  | '::cue-region'
  | '::file-selector-button'
  | '::first-letter'
  | '::first-line'
  | '::grammar-error'
  | '::marker'
  | '::placeholder'
  | '::selection'
  | '::spelling-error'
  | '::target-text'
  | '::view-transition'

export type CSSPseudoFunction =
  | ':current'
  | ':future'
  | ':has'
  | ':host'
  | ':host-context'
  | ':is'
  | ':lang'
  | ':not'
  | ':nth-child'
  | ':nth-last-child'
  | ':nth-last-of-type'
  | ':nth-of-type'
  | ':past'
  | ':where'
  | '::highlight'
  | '::part'
  | '::slotted'
  | '::view-transition-group'
  | '::view-transition-image-pair'
  | '::view-transition-new'
  | '::view-transition-old'

export type CSSPseudoRule = {
  [K in CSSPseudoProperty]?: CSSRule
} & {
  [K in CSSPseudoFunction as K | `${K}(${string})`]?: CSSRule
}

export type CSSPropertiesRuleArrayValue<T> = [value: T, important: boolean | 'important']

export type CSSPropertiesRuleObjectValue<T> = { value: T; important: boolean | 'important' }

export type CSSPropertiesRuleValue<T> = T | CSSPropertiesRuleArrayValue<T> | CSSPropertiesRuleObjectValue<T>

export type CSSPropertiesRule = {
  [K in keyof CSSProperties]?: CSSPropertiesRuleValue<CSSProperties[K] | null>
}

export type CSSSuperadditionRule = {
  [K: `&${string}`]: CSSRule
}

export type CSSRule = CSSPropertiesRule & CSSPseudoRule & CSSSuperadditionRule

export interface UseSheetOption {
  /**
   * 用于挂载到 CSSStyleSheet 实例上的标记，用于可能的复用
   * @default Symbol('useSheet')
   */
  id?: string | Symbol
  /**
   * 自动挂载/卸载。为 false 时需要使用者手动执行 mount/unmount 函数
   * @default true
   */
  automatic?: boolean
  /**
   * 挂载的文档对象
   */
  document?: DocumentOrShadowRoot
}

function isObject<T extends object>(target: any): target is T {
  return Object.prototype.toString.call(target) === '[object Object]'
}

// const selectorTextRegExp = [/(\.|\#|:{1,2})[-_a-zA-Z0-9]/, /(&|\+|\>|~|\|)[\0\t]*[-_a-zA-Z0-9]/, /[-_a-zA-Z0-9][\0\t]*,/, /\[.+?\]/];

// function isSelectorText(target: string) {
//   return selectorTextRegExp.some((regExp) => regExp.test(target));
// }

const selectorTextRegExp = /^(&|:{1,2}).+/

function isSelectorText(target: string) {
  return selectorTextRegExp.test(target)
}

const uppercaseRegExp = /[A-Z]/g

/**
 * 规范化 css 属性名
 * @param property css 属性名
 * @returns
 */
function normalizeCSSRuleProperty(property: string) {
  return property.replace(uppercaseRegExp, (char) => `-${char.toLowerCase()}`)
}

/**
 * 规范化 css 属性值
 * @param source css 属性值
 * @returns
 */
function normalizeCSSRuleValue(source: any): { value: string | null; important: 'important' | undefined } {
  const data: CSSPropertiesRuleObjectValue<string> = { value: source, important: false }

  if (isObject(source)) {
    Object.assign(data, source)
  } else if (Array.isArray(source)) {
    data.value = source[0]
    data.important = source[1]
  }

  const { value, important } = data

  return { value: value ?? null, important: important ? 'important' : void 0 }
}

/**
 * 规范化节点选择器字符串
 * @param selectorText 节点选择器字符串
 * @param superadditionText 追加的字符串
 * @returns
 */
function normalizeSelectorText(selectorText: string, superadditionText: string) {
  return superadditionText
    .replace(/&/g, selectorText)
    .replace(/\s{2,}/g, ' ')
    .split(',')
    .map((superaddition) => superaddition.trimEnd())
    .filter((superaddition) => superaddition.length)
    .map((superaddition) => `${selectorText}${superaddition}`)
}

export function useSheet(option: UseSheetOption = {}) {
  const { id = Symbol('useSheet'), automatic = true, document = window?.document } = option

  const sheets = [...document?.adoptedStyleSheets]
  const sheetIndex = sheets.findIndex((sheet) => sheet.__reuse === id)

  const sheet = sheets[sheetIndex] ?? new CSSStyleSheet()
  sheet.__reuse = id

  let _mounted = !!~sheetIndex
  let _trigger: () => void

  /**
   * sheet 挂载状态
   */
  const isMounted = customRef((track, trigger) => {
    _trigger = trigger
    return {
      get() {
        track()
        return _mounted
      },
      set() {}
    }
  })

  function mount() {
    if (!_mounted && document) {
      document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]
      _mounted = true
      _trigger()
    }
  }

  function unmount() {
    if (_mounted && document) {
      document.adoptedStyleSheets = Array.from(document.adoptedStyleSheets).filter((item) => item !== sheet)
      _mounted = false
      _trigger()
    }
  }

  function findRuleIndex(selectorText: string) {
    if (typeof selectorText !== 'string' || !selectorText.length) {
      throw new Error('selectorText 必须是有效的节点选择器字符串')
    }
    for (let index = 0; index < sheet.cssRules.length; index++) {
      const rule = sheet.cssRules[index] as CSSStyleRule
      if (rule.selectorText === selectorText) return index
    }
    return -1
  }

  /**
   * 获取样式选择器对应的 CSSStyleRule 对象
   * @param selectorText 样式选择器
   * @param autoCreate 当样式选择器的 CSSStyleRule 对象不存在时，是否自动创建
   */
  function getRule(selectorText: string, autoCreate?: false): CSSStyleRule | undefined
  function getRule(selectorText: string, autoCreate: true): CSSStyleRule
  function getRule(selectorText: string, autoCreate?: boolean) {
    const ruleIndex = findRuleIndex(selectorText)

    if (!~ruleIndex && autoCreate) {
      const index = sheet.cssRules.length
      sheet.insertRule(`${selectorText} {}`, index)
      return sheet.cssRules[index] as CSSStyleRule
    }

    return sheet.cssRules[ruleIndex] as CSSStyleRule
  }

  /**
   * 设置样式选择器对应的样式规则
   * @param selectorText 样式选择器
   * @param css 样式对象
   */
  function setRule(selectorText: string, css: CSSRule) {
    /** 待设置队列 */
    const queue: { selector: string; cssRule: CSSRule }[] = [{ selector: selectorText, cssRule: css }]

    while (queue.length) {
      const { selector, cssRule } = queue.shift()!

      const rule = getRule(selector, true)
      Object.entries(cssRule).forEach(([property, source]) => {
        if (isSelectorText(property)) {
          const selectorList = normalizeSelectorText(selector, property)
          if (isObject<CSSRule>(source)) {
            selectorList.forEach((text) => {
              queue.push({ selector: text, cssRule: source })
            })
          } else {
            selectorList.forEach(removeRule)
          }

          return
        }

        const { value, important } = normalizeCSSRuleValue(source)
        rule.style.setProperty(normalizeCSSRuleProperty(property), value, important)
      })
    }
  }

  /**
   * 移除样式选择器对应的 CSSStyleRule 对象
   * @param selectorText 样式选择器
   */
  function removeRule(selectorText: string) {
    const ruleIndex = findRuleIndex(selectorText)
    ~ruleIndex && sheet.deleteRule(ruleIndex)
  }

  function clearRule() {
    sheet.replace('')
  }

  if (automatic) {
    mount()
    getCurrentInstance() && onScopeDispose(unmount)
  }

  return { id, sheet, isMounted, mount, unmount, getRule, setRule, removeRule, clearRule }
}
